package com.iteaj.iot.server;

import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import com.iteaj.iot.*;
import com.iteaj.iot.codec.SocketMessageDecoder;
import com.iteaj.iot.codec.filter.CombinedInterceptor;
import com.iteaj.iot.codec.filter.Interceptor;
import com.iteaj.iot.codec.filter.RegisterParams;
import com.iteaj.iot.config.ConnectProperties;
import com.iteaj.iot.server.protocol.ClientInitiativeProtocol;
import io.netty.bootstrap.AbstractBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.util.ReferenceCounted;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Constructor;
import java.util.Optional;

import static com.iteaj.iot.CoreConst.COMPONENT;

/**
 * 用来封装需要监听在TCP端口的设备的所需要的服务端组件
 */
public abstract class SocketServerComponent<M extends ServerMessage, R extends ReferenceCounted> extends SocketProtocolFactory<M>
        implements ServerComponent, IotSocketServer, SocketMessageDecoder<R>, CombinedInterceptor {

    /**
     * 是否启用
     */
    private boolean start;

    /**
     * 开启时间
     */
    private long startTime;

    /**
     * 拦截器
     */
    private CombinedInterceptor interceptor;

    /**
     * 服务端Channel
     * @see io.netty.channel.ServerChannel
     */
    private Channel channel;
    /**
     * 如果设备编号重复 是否覆改掉上一台
     */
    private boolean override;
    private Class<M> messageClass;
    private Constructor<M> constructor;
    private SocketDeviceManager deviceManager;
    private ConnectProperties connectProperties;
    protected Logger logger = LoggerFactory.getLogger(getClass());

    public SocketServerComponent(ConnectProperties connectProperties) {
        this.override = true;
        this.connectProperties = connectProperties;
        this.setDelegation(protocolTimeoutStorage());
    }

    public SocketServerComponent(ConnectProperties connectProperties, CombinedInterceptor interceptor) {
        this.override = true;
        this.interceptor = interceptor;
        this.connectProperties = connectProperties;
        this.setDelegation(protocolTimeoutStorage());
    }

    /**
     * 开启监听端口并且绑定
     * @param sb
     */
    protected void doBind(AbstractBootstrap sb) {
        ChannelFuture bind;
        if(StrUtil.isNotBlank(config().getHost())) {
            bind = sb.bind(config().getHost(), config().getPort());
        } else {
            bind = sb.bind(config().getPort());
        }

        // 绑定此设备要开启的端口
        this.channel = bind.addListener(future -> {
            Integer port = this.config().getPort();
            String host = this.config().getHost() == null ? "0.0.0.0" : this.config().getHost();
            if(future.isSuccess()) {
                LOGGER.info("监听端口成功({}) 主机：{}:{} - 简介：{}", getName(), host, port, getDesc());
            } else {
                LOGGER.error("监听端口失败({}) 主机: {}:{} - 简介：{} - 异常信息: {}", getName(), host, port
                        , getDesc(), future.cause().getMessage(), future.cause());
            }
        }).syncUninterruptibly().channel();
        this.channel.attr(COMPONENT).set(this);
    }

    /**
     * 自定义Handler
     * 注：自定义handler的时候需要指定放到那个handler的后面, 整个iot框架对handler的顺序是有要求的
     * @param p
     */
    protected void doInitChannel(ChannelPipeline p) {
        // 加入ssl支持
        if(config().getSsl() != null) {
//            SslContextBuilder.forServer()
            p.addFirst("IOT_SSL", new SslHandler(null));
        }
    }

    @Override
    public void start(Object config) {
        if(!this.isStart()) {
            this.setStart(true);
            this.startTime = System.currentTimeMillis();
            this.deviceManager = createDeviceManager();

            if(config instanceof Interceptor) {
                this.interceptor = (CombinedInterceptor) config;
            }

            if(this.interceptor == null) {
                this.interceptor = CombinedInterceptor.DEFAULT;
            }

            AbstractBootstrap abstractBootstrap = this.initParentChannel();
            this.doBind(abstractBootstrap);
        }
    }

    /**
     * 初始化父Channel
     * @return
     */
    protected abstract AbstractBootstrap initParentChannel();

    @Override
    public ConnectProperties config() {
        return this.connectProperties;
    }

    @Override
    protected abstract ClientInitiativeProtocol<M> doGetProtocol(M message, ProtocolType type);

    /**
     * 写出报文
     * @param equipCode 设备编号
     * @param msg 发送的协议
     * @param args 自定义参数
     * @return
     */
    public Optional<ChannelFuture> writeAndFlush(String equipCode, Object msg, Object... args) {
        return getDeviceManager().writeAndFlush(equipCode, msg, args);
    }

    /**
     * 写出协议
     * @see Protocol#requestMessage() 请求的报文
     * @see Protocol#responseMessage() 响应的报文
     * @param equipCode 设备编号
     * @param protocol 要写出的协议
     * @return
     */
    public Optional<ChannelFuture> writeAndFlush(String equipCode, Protocol protocol) {
        return this.writeAndFlush(equipCode, protocol, null);
    }

    @Override
    public SocketDeviceManager getDeviceManager() {
        return this.deviceManager;
    }

    /**
     * 创建协议工厂
     * @return
     */
    @Override
    public IotProtocolFactory protocolFactory() {
        return this;
    }

    /**
     * 创建设备管理器
     * @return
     */
    abstract protected SocketDeviceManager createDeviceManager();

    @Override
    public M createMessage(byte[] message) {
        try {
            return resolveConstructor().newInstance(message);
        } catch (ReflectiveOperationException e) {
            throw new FrameworkException(e);
        }
    }

    @Override
    public boolean isDecoder(Channel channel, ReferenceCounted msg) {
        return getInterceptor().isDecoder(channel, msg);
    }

    @Override
    public Object idle(String deviceSn, IdleState state) {
        return getInterceptor().idle(deviceSn, state);
    }

    @Override
    public boolean isActivation(Channel channel, FrameworkComponent component) {
        return getInterceptor().isActivation(channel, component);
    }

    @Override
    public Message.MessageHead register(Message.MessageHead head, RegisterParams params) {
        return getInterceptor().register(head, params);
    }

    @Override
    public CombinedInterceptor getInterceptor() {
        return this.interceptor;
    }

    public void setInterceptor(CombinedInterceptor interceptor) {
        this.interceptor = interceptor;
    }

    /**
     * 设备服务端使用的报文类
     * 此类将用来标识唯一的设备服务组件
     * @return
     */
    @Override
    public Class<M> getMessageClass() {
        if(this.messageClass == null) {
            this.messageClass = (Class<M>) ClassUtil.getTypeArgument(getClass(), 0);
            if(this.messageClass == null) {
                throw new IllegalArgumentException("未指定泛型["+getClass().getSimpleName()+"<M>]");
            }

            this.resolveConstructor();
        }

        return this.messageClass;
    }

    private Constructor<M> resolveConstructor() {
        if(this.constructor == null) {
            try {
                this.constructor = this.getMessageClass().getConstructor(byte[].class);
            } catch (NoSuchMethodException e) {
                final String simpleName = this.getMessageClass().getSimpleName();
                throw new ProtocolException("报文类型缺少构造函数["+simpleName+"(byte[])]", e);
            }
        }

        return this.constructor;
    }

    protected final ProtocolTimeoutStorage protocolTimeoutStorage() {
        if(getDelegation() != null) {
            return this.getDelegation();
        }

        return doCreateProtocolTimeoutStorage();
    }

    /**
     * 创建超时管理器
     * @return
     */
    protected ProtocolTimeoutStorage doCreateProtocolTimeoutStorage() {
        return new ProtocolTimeoutStorage(getName());
    }

    @Override
    public long startTime() {
        return startTime;
    }

    /**
     * 覆盖上一台相同设备编号的设备
     * @return
     */
    public boolean isOverride() {
        return override;
    }

    public void setOverride(boolean override) {
        this.override = override;
    }

    protected Channel getChannel() {
        return this.channel;
    }

    protected void setChannel(Channel channel) {
        this.channel = channel;
    }

    @Override
    public void close() {
        if(this.getChannel() != null) {
            this.getChannel()
                    .close()
                    .syncUninterruptibly()
                    .addListener(future -> {
                        this.setStart(false);
                        String host = this.config().getHost() == null ? "0.0.0.0" : this.config().getHost();
                        if(future.isSuccess()) {
                            logger.info("关闭组件成功({}) 主机: {}:{} - 简介: {}", getName(), host, config().getPort(), getDesc());
                        } else {
                            logger.error("关闭组件失败({}) 主机: {}:{} - 简介: {}", getName(), host, config().getPort(), getDesc(), future.cause());
                        }
                    });
        }
    }

    @Override
    public boolean isStart() {
        return start;
    }

    protected void setStart(boolean start) {
        this.start = start;
    }
}
